package roborally;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

import Exceptions.ElementPlacementError;
import Exceptions.IllegalCoordinateException;
import Exceptions.IllegalDimensionException;
import Exceptions.MergeError;
import Exceptions.PickupError;
import Exceptions.ProgramError;
import Exceptions.ShootException;
import Exceptions.TerminationError;
import Exceptions.TransferError;

/**
 * @author Jef Keijers/Bachelor of Science: Informatics
 * @version 1.0
 */
public class Facade implements IFacade<Board, Robot, Wall, Battery, RepairKit, SurpriseBox> {

	@Override
	public Board createBoard(long width, long height)  {
		try {
			return new Board(width,height);
		} catch (IllegalDimensionException e) {
			return null;
		}
	}

	@Override
	public void merge(Board board1, Board board2) {
		try {
			board1.merge(board2);
		} catch (Exception e) {
			System.out.println("Couldn't merge the two boards!");
		}
	}

	@Override
	public Battery createBattery(double initialEnergy, int weight) {
		if (weight>=0) 
			return new Battery(initialEnergy,weight);
		return null;
		
	}

	@Override
	public void putBattery(Board board, long x, long y, Battery battery) {
		try {
			battery.putItem(board, x, y);
		} catch (Exception e) {
			System.out.println("Couldn't place the battery on position " +x+","+y);
		}
	}

	@Override
	public long getBatteryX(Battery battery) throws IllegalStateException {
		return battery.getPosition().getX();
	}

	@Override
	public long getBatteryY(Battery battery) throws IllegalStateException {
		return battery.getPosition().getY();
	}

	@Override
	public RepairKit createRepairKit(double repairAmount, int weight) {
		if ((repairAmount>0) && (weight>=0))
			return new RepairKit(repairAmount, weight);
		return null;
	}

	@Override
	public void putRepairKit(Board board, long x, long y, RepairKit repairKit) {
		try {
			repairKit.putItem(board, x, y);
		} catch (Exception e) {
			System.out.println("Couldn't place the repairkit on position " +x+","+y);
		}
	}

	@Override
	public long getRepairKitX(RepairKit repairKit) throws IllegalStateException {
		return repairKit.getPosition().getX();
	}

	@Override
	public long getRepairKitY(RepairKit repairKit) throws IllegalStateException {
		return repairKit.getPosition().getY();
	}

	@Override
	public SurpriseBox createSurpriseBox(int weight) {
		if (weight>=0)
			return new SurpriseBox(weight);
		return null;
	}

	@Override
	public void putSurpriseBox(Board board, long x, long y,
			SurpriseBox surpriseBox) {
		try {
			surpriseBox.putItem(board, x, y);
		} catch (Exception e) {
			System.out.println("Couldn't place the surprisebox on position " +x+","+y);
		}
	}

	@Override
	public long getSurpriseBoxX(SurpriseBox surpriseBox)
			throws IllegalStateException {
		return surpriseBox.getPosition().getX();
	}

	@Override
	public long getSurpriseBoxY(SurpriseBox surpriseBox)
			throws IllegalStateException {
		return surpriseBox.getPosition().getY();
	}

	@Override
	public Robot createRobot(int orientation, double initialEnergy)  {
		if (initialEnergy>=0) 
			return new Robot(Direction.getDirection(orientation),initialEnergy);
		return null;
	}

	@Override
	public void putRobot(Board board, long x, long y, Robot robot) {
		try {
			robot.putRobot(board, x, y);
		} catch (Exception e) {
			System.out.println("Couldn't place the robot on position " +x+","+y);
		}
	}

	@Override
	public long getRobotX(Robot robot) throws IllegalStateException {
		return robot.getPosition().getX();
	}

	@Override
	public long getRobotY(Robot robot) throws IllegalStateException {
		return robot.getPosition().getY();
	}

	@Override
	public int getOrientation(Robot robot) {
		return robot.getOrientation().getCode();
	}

	@Override
	public double getEnergy(Robot robot) {
		return robot.getEnergy().getValue();
	}

	@Override
	public void move(Robot robot) {
		try {
			robot.moveForward();
		} catch (Exception e) {
			System.out.println("Couldn't move the robot!");	
		}
	}

	@Override
	public void turn(Robot robot) {
		robot.turn(Direction.RIGHT);
	}

	@Override
	public void pickUpBattery(Robot robot, Battery battery) {
		try {
			robot.pickUp(battery);
		} catch (PickupError e) {
			System.out.println("Couldn't pickup the battery!");
		}
	}

	@Override
	public void useBattery(Robot robot, Battery battery) {
		try {
			robot.use(battery);
		} catch (Exception e) {
			System.out.println("Couldn't use the battery!");
		}
	}

	@Override
	public void dropBattery(Robot robot, Battery battery) {
		try {
			robot.drop(battery);
		} catch (IllegalCoordinateException e) {
			System.out.println("Couldn't drop the battery on position " +robot.getPosition().getX()+","+robot.getPosition().getX());
		} catch (ElementPlacementError e) {
			System.out.println("Couldn't drop the battery on position " +robot.getPosition().getX()+","+robot.getPosition().getX());
		}
	}

	@Override
	public void pickUpRepairKit(Robot robot, RepairKit repairKit) {
		try {
			robot.pickUp(repairKit);
		} catch (PickupError e) {
			System.out.println("Couldn't pickup the repairkit!");
		}
	}

	@Override
	public void useRepairKit(Robot robot, RepairKit repairKit) {
		try {
			robot.use(repairKit);
		} catch (Exception e) {
			System.out.println("Couldn't use the repairkit!");
		}
	}

	@Override
	public void dropRepairKit(Robot robot, RepairKit repairKit) {
		try {
			robot.drop(repairKit);
		} catch (IllegalCoordinateException e) {
			System.out.println("Couldn't drop the repairkit on position " +robot.getPosition().getX()+","+robot.getPosition().getX());
		} catch (ElementPlacementError e) {
			System.out.println("Couldn't drop the repairkit on position " +robot.getPosition().getX()+","+robot.getPosition().getX());
		}
	}

	@Override
	public void pickUpSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		try {
			robot.pickUp(surpriseBox);
		} catch (PickupError e) {
			System.out.println("Couldn't pickup the surprisebox!");
		}
	}

	@Override
	public void useSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		try {
			robot.use(surpriseBox);
		} catch (Exception e) {
			System.out.println("Couldn't use the surprisebox!");
		}
	}

	@Override
	public void dropSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		try {
			robot.drop(surpriseBox);
		} catch (IllegalCoordinateException e) {
			System.out.println("Couldn't drop the surprisebox on position " +robot.getPosition().getX()+","+robot.getPosition().getX());
		} catch (ElementPlacementError e) {
			System.out.println("Couldn't drop the surprisebox on position " +robot.getPosition().getX()+","+robot.getPosition().getX());
		}
	}

	@Override
	public void transferItems(Robot from, Robot to) {
		try {
			from.transferPossessionsTo(to);
		} catch (TransferError e) {
			System.out.println("Transfer failure: the two robot's do not stand next to each other!");
		}
	}

	@Override
	public int isMinimalCostToReach17Plus() {
		return 0;  // we houden GEEN rekening met other robots,walls and turning;
	}

	@Override
	public double getMinimalCostToReach(Robot robot, long x, long y) {
		try {
			return robot.getEnergyRequiredToReachPosition(new Position(x,y,robot.getBoard()));
		} catch (IllegalCoordinateException e) {
			System.out.println("The robot cannot reach the given position: so there's no minimal cost!");
			return -1;
		}
	}

	@Override
	public int isMoveNextTo18Plus() {
		return 0;  // we houden GEEN rekening met other robots,walls and turning;
	}

	@Override
	public void moveNextTo(Robot robot, Robot other) {
		try {
			robot.moveTo(other);
		} catch (IllegalCoordinateException e) {
			System.out.println("Robots couldn't move to each other (due to an illegal coordinate)!");
		} catch (TerminationError e) {
			System.out.println("Robots couldn't move to each other!");
		} catch (ElementPlacementError e) {
			System.out.println("Robots couldn't move to each other (because one or both robot(s) can't move)!");
		}
	}

	@Override
	public void shoot(Robot robot) throws UnsupportedOperationException {
		
			try {
				robot.shoot();
			} catch (ShootException e) {
				System.out.println("Couldn't shoot at one or more positions!");
			} catch (TerminationError e) {
				System.out.println("Couldn't terminate the element that was hit by the laser!");

			}
		
	}

	@Override
	public Wall createWall() throws UnsupportedOperationException {
		return new Wall();
	}

	@Override
	public void putWall(Board board, long x, long y, Wall wall)
			throws UnsupportedOperationException {
		try {
			wall.putWall(board, x, y);
		} catch (ElementPlacementError e) {
			System.out.println("Couldn't put the wall on position " +wall.getPosition().getX()+","+wall.getPosition().getX());
		}
	}

	@Override
	public long getWallX(Wall wall) throws IllegalStateException,
			UnsupportedOperationException {
		return wall.getPosition().getX();
	}

	@Override
	public long getWallY(Wall wall) throws IllegalStateException,
			UnsupportedOperationException {
		return wall.getPosition().getY();
	}

	@Override
	public Set<Robot> getRobots(Board board) {
		
		Set<Robot> robots=new HashSet<Robot>();
		
		for (Position positions:board.getPositionMap().keySet()) {
			LinkedList <Element> listOnPosition = board.getPositionMap().get(positions);
			
			for (Element elements:listOnPosition) {
				if (elements.getClass()==Robot.class)
					robots.add((Robot) elements);
			}			
		}
		return robots;
	}

	@Override
	public Set<Wall> getWalls(Board board) throws UnsupportedOperationException {
		Set<Wall> walls=new HashSet<Wall>(); 
		
		for (Position positions:board.getPositionMap().keySet()) {
			LinkedList <Element> listOnPosition = board.getPositionMap().get(positions);
			
			for (Element elements:listOnPosition) {
				if (elements.getClass()==Wall.class)
					walls.add((Wall) elements);
			}			
		}
		
		return walls;
	}

	@Override
	public Set<RepairKit> getRepairKits(Board board) {
		Set<RepairKit> repairkits=new HashSet<RepairKit>();
		
		for (Position positions:board.getPositionMap().keySet()) {
			LinkedList <Element> listOnPosition = board.getPositionMap().get(positions);
			
			for (Element elements:listOnPosition) {
				if (elements.getClass()==RepairKit.class)
					repairkits.add((RepairKit) elements);
			}			
		}
		
		return repairkits;
	}

	@Override
	public Set<SurpriseBox> getSurpriseBoxes(Board board) {
		Set<SurpriseBox> surpriseboxes=new HashSet<SurpriseBox>();
		
		for (Position positions:board.getPositionMap().keySet()) {
			LinkedList <Element> listOnPosition = board.getPositionMap().get(positions);
			
			for (Element elements:listOnPosition) {
				if (elements.getClass()==SurpriseBox.class)
					surpriseboxes.add((SurpriseBox) elements);
			}			
		}
		
		return surpriseboxes;
	}

	@Override
	public Set<Battery> getBatteries(Board board) {
		Set<Battery> batteries=new HashSet<Battery>();
		
		for (Position positions:board.getPositionMap().keySet()) {
			LinkedList <Element> listOnPosition = board.getPositionMap().get(positions);
			
			for (Element elements:listOnPosition) {
				if (elements.getClass()==Battery.class)
					batteries.add((Battery) elements);
			}			
		}
		
		return batteries;
	}

	@Override
	public int loadProgramFromFile(Robot robot, String path)  {
		try {
			robot.loadProgram(path);
		} catch (IOException e) {
			System.out.println("Couldn't load the program!");
			return -1;
		} catch (ProgramError e) {
			System.out.println("There's something wrong with your program!");
		}
		return 0;
	}

	@Override
	public int saveProgramToFile(Robot robot, String path) {
        try {
               BufferedWriter writer = new BufferedWriter(new FileWriter(path));
               writer.write(robot.getProgram().toString());
        } catch ( IOException e) {
        	return -1;
        }
        return 0;
	}

	@Override
	public void prettyPrintProgram(Robot robot, Writer writer) {
		try {
			writer.append(robot.getProgram().toString());
		} catch (IOException e) {
			System.out.println("Couldn't print the program prettily!");
		}
	}

	@Override
	public void stepn(Robot robot, int n) {
		try {		
			while (n>0) {
				robot.executeOneStep();
				n--;
			}
		} catch (PickupError e) {
			System.out.println("Couldn't pickup an item!");
		} catch (Exception e) {
			System.out.println("Couldn't execute the program further; maybe the condition doesn't meet!");
		} 
	}
	
	public double getEnergyBattery(Battery battery) {
		return battery.getEnergy().getValue();
	}	
}

